package com.googlecode.ascrblr.cli.conf;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.logging.Logger;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.io.FilenameUtils;

/**
 * 
 * 
 */
public class ConfigurationFactory {
	
	public static final File DIR_THIS = new File(".");
	public static final File DIR_PARENT = new File("..");
	public static final String SYSTEM_SEPARATOR= FilenameUtils.separatorsToSystem("/");

	
	private final Logger logger = Logger.getLogger(ConfigurationFactory.class
			.getName());

	private static final Predicate FILTER_EMPTYSTRING = new Predicate() {

		/*
		 * (non-Javadoc)
		 * 
		 * @see org.apache.commons.collections.Predicate#evaluate(java.lang.Object)
		 */
		public boolean evaluate(Object object) {

			if (!(object instanceof String)) {
				return false;
			}

			return !isEmpty((String) object);

		}

		/**
		 * @param a
		 *            String
		 * @return true if string is null or empty
		 */
		private boolean isEmpty(String string) {
			return (string == null || "".equals(string.trim()));
		}
	};

	private static final ConfigurationFactory instance = new ConfigurationFactory();

	private PosixParser parser = null;
	private Options options = null;

	/**
	 * 
	 */
	private ConfigurationFactory() {
		super();
		options = CommandLineOption.getOptions();
		parser = new PosixParser();
	}

	/**
	 * @return
	 */
	public static ConfigurationFactory getInstance() {
		return instance;
	}

	/**
	 * Creates a new Configuration object.
	 * 
	 * @param args
	 *            the args
	 * 
	 * @return the configuration bean
	 * @throws ParseException
	 */
	public ConfigurationBean createConfiguration(String[] args)
			throws ConfigurationException {

		// check if args is empty
		if (args == null || args.length == 0) {
			throw new ConfigurationException("args is null");
		}
		
		ArrayList<String> argList = null;
		
		// delete null and empty terms from args
		try {
			argList = new ArrayList<String>(Arrays.asList(args));
			CollectionUtils.filter(argList, FILTER_EMPTYSTRING);
			if (argList.isEmpty()) {
				throw new IllegalArgumentException("Arglist is empty");
			}
		} catch (Exception e) {
			throw new ConfigurationException("createConfiguration: args is empty '"
					+ args + "'");
		}
		
		args = argList.toArray(new String[argList.size()]);


		CommandLine line = null;
		try {
			line = parser.parse(options, args);
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			logger.warning(e.getMessage());
			throw new IllegalStateException(e);
		}
		
		ConfigurationBean configurationBean = new ConfigurationBean();
		
		boolean showHelp = line
		.hasOption(CommandLineOption.HELP.getOption().getOpt());
		
		configurationBean.setShowHelp(showHelp);
		if (showHelp) {
			return configurationBean;
		}
	

		try {
			configurationBean
			.setUser(readValue(line, CommandLineOption.LASTFM_USER));
		} catch (IllegalArgumentException e) {
			configurationBean.setValid(false);
		}
		
		try {
			configurationBean.setPassword(readValue(line,
					CommandLineOption.LASTFM_PASSWD));
		} catch (IllegalArgumentException e) {
			configurationBean.setValid(false);
		}
		
		configurationBean.setRecursive(line
				.hasOption(CommandLineOption.RECURSIVE.getOption().getOpt()));
		
		
		
		// remove options and reset args
		args = line.getArgs();
		
		if ( (args == null || args.length == 0 )) {
			// TODO: localize
			throw new ConfigurationException("No Files given!");
		}
		
		ArrayList<String> remainingArgs = new ArrayList<String>(Arrays.asList(args));
		remainingArgs = normalizeArguments(remainingArgs);

		try {
			configurationBean.setArguments(remainingArgs);
		} catch (IllegalArgumentException e) {
			configurationBean.setValid(false);
		}


		
		
		//configurationBean.setArguments();

		return configurationBean;
	}

	/**
	 * @param line
	 * @param cliOpt
	 * @return
	 */
	private String readValue(CommandLine line, CommandLineOption cliOpt) {
		String value = null;
		String option = cliOpt.getOption().getOpt();

		if (line.hasOption(option)) {
			value = line.getOptionValue(option);
		} else {
			value = System.getenv(cliOpt.name());
		}

		if (value != null) {
			value = value.trim();
		}

		return value;
	}



	/**
	 * @param a
	 *            String
	 * @return true if string is null or empty
	 */
	private boolean isEmpty(String string) {
		return (string == null || "".equals(string.trim()));
	}
	
	
	/**
	 * Sets the arguments.
	 * 
	 * @param arguments the new arguments
	 */
	public ArrayList<String> normalizeArguments(ArrayList<String> arguments) {
		String userDir = System.getProperty("user.dir");
		String separator = FilenameUtils.separatorsToSystem("/");
		
		ArrayList<String> normalized = new ArrayList<String>();
		
		for (String filename : arguments) {
			if (filename == null || "".equals(filename.trim())) {
				continue;
			}
			
			if (isRelativePath(filename))  {
				filename = FilenameUtils.separatorsToSystem(userDir + "/" +  filename);
			}
			normalized.add(FilenameUtils.normalizeNoEndSeparator(filename));
		}
		
		return normalized;
	}
	
	/**
	 * Checks if is relative path.
	 * 
	 * @param path the path
	 * 
	 * @return true, if is relative path
	 */
	public static final boolean isRelativePath(String path) {
		if (path == null || "".equals(path)) {
			throw new IllegalArgumentException("path must not be empty");
		}
		String prefix = FilenameUtils.getPrefix(path);
		
		
		if (prefix != null && !"".equals(prefix)) {
			// we have a prefix ....
			if (prefix.indexOf(":") == 1 ) {
				return false;
			}
			
			if (prefix.startsWith("~")) {
				return false;
			}
		}
		else {
			return true;
		}
		
		
		
		// relativ pathes start with:
		// .
		// ..
		// just the file or folder
		
		// absolute pathes start with:
		// separator
		// on Windows: one letter followed by ":\"
		
		return (path.startsWith(DIR_THIS.getName())); // || path.startsWith(FilenameUtils.)))
		
	}

	
	
}
